﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using GRMS.App_Code;
using System.Text.RegularExpressions;
using System.IO;
using System.Xml.Serialization;
using System.Globalization;
using System.Runtime.Serialization;
using GRMS.App_Data;

namespace GRMS
{
    [DataContract]
    public class Attribute
    {
        #region Properteries
        [DataMember]
        private bool _baseSet;
        [DataMember]
        private bool _isNumber;
        [DataMember]
        private string _id;
        [DataMember]
        private string _initial;
        [DataMember]
        private string _base;
        [DataMember]
        private string _mod = "0";
        [DataMember]
        private string _stun = "0";
        [DataMember]
        private string _section;
        [DataMember]
        private string _type;
        [DataMember]
        private string skillcheck;
        [DataMember]
        private OrderedTable attributes;
        [DataMember]
        private Character _parent;
        #endregion

        #region Geters
        public bool IsNumber { get { return _isNumber; } }
        public string ID { get { return _id; } }
        public string Base { get { return _base.ToString(); } }
        public string Mod { get { return _mod.ToString(); } }
        public bool Has(string attr)
        {
            return this.Attributes.ContainsKey(attr);
        }
        private Hashtable Attributes
        {
            get
            {
                return attributes.HashTable;
            }
            set
            {
                attributes = new OrderedTable(value);
            }
        }
        public string Value
        {
            get
            {
                if (this.Attributes.ContainsKey("note") && this.attributes["note"].ToString() == "check")
                {
                    string[] Split = this._initial.Split('+');
                    string result = _initial;
                    foreach (string s in Split)
                        result = result.Replace(s.Trim(), _parent.Properties[s.Trim()].Value);

                   return result;
                }
                else if (this.ID == "Life Points")
                    return string.Format("Stun: {0} | Wound: {1}", this._stun, this._mod);
                else if (this._section == "weapons")
                    return this._base;
                else if (this._baseSet && this._section != "brief")
                    return this.GetCheck((Convert.ToInt32(this._base) + Convert.ToInt32(this._mod)).ToString());
                else
                    return this._base;
            }
            set
            {
                if (this.Attributes.ContainsKey("note") && this.attributes["note"].ToString() == "check")
                { }
                else if (this.ID == "Life Points")
                {
                    string[] values = value.Split('|');
                    this._stun = values[0].Replace("Stun:", "").Trim();
                    this._mod = values[1].Replace("Wound:", "").Trim();
                }
                else if (this._baseSet && this._section != "brief")
                {
                    string[] values = value.Split('|');
                    this._base = values[0].Replace("Base:", "").Trim();
                    this._mod = values[1].Replace("Mod:", "").Trim();
                }
                else
                    this._base = value;
            }
        }
        public string Section 
        { 
            get 
            {
                if (this._section == "skills")
                    return this.Get("type").Value;
                else
                    return this._section; 
            } 
        }

        internal bool Skilled
        {
            get
            {
                if (this.Attributes.ContainsKey("skilled"))
                    return true;
                return false;
            }
        }
        public KeyValuePair<string, string> Get(string key)
        {
            if (this.Attributes.ContainsKey(key))
                return new KeyValuePair<string, string>(key, this.attributes[key].ToString());
            return new KeyValuePair<string,string>();
        }
        public KeyValuePair<string, string> Get()
        {
            if (this._baseSet)
            {
                if (this.Value.Contains('+'))
                {
                    char[] trim = { ' ', '+' };
                    string[] s = this.Value.Split('+');
                    string Result = "";
                    foreach (string str in s)
                        Result += str.Trim() + " + ";
                    return new KeyValuePair<string, string>(this._id, Result.TrimEnd(trim));
                }
                else
                    return new KeyValuePair<string, string>(this._id, this.Value);
            }
            else
                return new KeyValuePair<string, string>(this._id, this._base);
        }
        private string GetCheck(string str)
        {
            string output = CommonCommands.Option(skillcheck, str.Trim()).Trim('\'');
            string result = "";
            if (output.Contains('('))
            {
                string i = str;
                while (output.Contains('('))
                {
                    string j = OptionSplit(output, ref result, true);
                    i = (Convert.ToInt32(i) + Convert.ToInt32(j)).ToString();
                    output = CommonCommands.Option(skillcheck, i);
                    result += output.Trim('\'');
                }
                return result;
            }
            else
                return output;
        }
        private string OptionSplit(string value, ref string result, bool IsDie)
        {
            char[] trim = { ')', '\'', '}' };
            string temp;
            if (IsDie)
            {
                result += Regex.Match(value, @"d[0-9]+ \+ ").Value;
                temp = value.Replace("Result", "").Replace(Regex.Match(value, @"d[0-9]+ \+ ").Value, "").Trim('(').Trim(')');
            }
            else
            {
                result += Regex.Match(value, @"[0-9]+ \+ ").Value;
                temp = value.Replace("Result", "").Replace(Regex.Match(value, @"[0-9]+ \+ ").Value, "").Trim('(').Trim(')');
            }
            int remainder = Convert.ToInt32(temp.Trim(trim).Replace(" ", ""));

            return (remainder).ToString().Trim('\'');
        }
        #endregion

        #region Constructors
        public Attribute(string key, Hashtable ht, string section, string skillcheck, Character Parent)
        {
            this._id = key;
            this._section = section;
            this._initial = ht[key].ToString();
            this._baseSet = false;
            this.skillcheck = skillcheck;
            this._parent = Parent;
            ht.Remove(key);
            this.attributes = new OrderedTable(new Hashtable(ht));
        }
        private Attribute() { }
        #endregion

        #region Modifiers
        public bool SetBase()
        {
            string Str = this._initial.Trim();
            int Num = 0;
            string Str1 = "";
            if (Str == "")
                return true;
            else if (int.TryParse(Str, out Num))
            {
                this._base = Num.ToString();
                this._baseSet = true;
                return true;
            }
            else
            {
                string[] Split = Str.Split('+');
                if (this.Attributes.ContainsKey("section") && !this.Attributes.ContainsKey("note") && (this.attributes["section"].ToString() == "derived_attributes" || this.attributes["section"].ToString() == "shock_points"))
                {
                    Dictionary<string, Attribute> Parent = this._parent.Properties;
                    foreach (string s in Split)
                        if ((Parent[s.Trim()])._baseSet)
                            Num += Convert.ToInt32((Parent[s.Trim()])._base);
                        else
                            return false;

                    this._base = Num.ToString();
                }
                else if (this.Attributes.ContainsKey("section") && this.Attributes.ContainsKey("note") && (this.attributes["section"].ToString() == "derived_attributes" || this.attributes["section"].ToString() == "shock_points"))
                {
                    Dictionary<string, Attribute> Parent = this._parent.Properties;
                    foreach (string s in Split)
                        if ((Parent[s.Trim()])._baseSet)
                            Str = Str.Replace(s.Trim(), "d" + (Parent[s.Trim()])._base);
                        else
                            return false;

                    this._base = Num.ToString();
                }
                else
                {
                    this._base = this._initial;
                }
                this._baseSet = true;

                if (this._id == "Life Points")
                {
                    this._base = (Int32.Parse(this._base) * 2).ToString();
                    this._stun = this._base;
                    this._mod = this._base;
                }
                
                return true;
            }
        }
        public void ModBase(string value)
        {
            this._base = value;
        }
        public void ModMod(string value)
        {
            this._mod = (int.Parse(this._mod) + int.Parse(value)).ToString();
        }
        public void ModStun(string value)
        {
            this._stun = (int.Parse(this._stun) + int.Parse(value)).ToString();
        }
        internal void Reset()
        {
            this._mod = this._base;
            this._stun = this._base;
        }
        public void ChangeProperty(string value)
        {
            this._mod = value;
        }
        public bool Increment(Session session)
        {
            return false;
        }
        #endregion
    }

    [DataContract(IsReference = true)]
    public class Character
    {
        #region Character Properties
        [DataMember]
        private string _id;
        [DataMember]
        public string ID
        {
            get { return _id; }
            set { _id = value; }
        }
        [DataMember]
        private OrderedTable properties;
        public Dictionary<string, Attribute> Properties 
        { 
            get 
            { 
                return properties.Dictionary; 
            }
            set
            {
                properties = new OrderedTable(value);
            }
        }
        [DataMember]
        private List<string> dependentProperties;

        [DataMember]
        private string CSSStyle;
        [DataMember]
        private string skillcheck;
        [DataMember]
        private object p;
        private Session sesh;
        private Hashtable hashtable;
        #endregion

        #region Constructors
        private Character() { }
        public Character(Session session)
        {
            string templateFile = "/App_Data/Rules/CharacterSheet.xml";

            this.CSSStyle = XMLReader.GetValueFrom(templateFile, "sheet system CSSstyle", session)["value"].ToString();
            //this.layout = new CharacterSheet(XMLReader.GetValueFrom(templateFile, "sheet system layout", session)["value"].ToString());
            this.skillcheck = XMLReader.GetValueFrom(templateFile, "sheet system skillcheck", session)["value"].ToString();

            Dictionary<string, Hashtable> attr = XMLReader.GetValuesFrom(templateFile, "sheet section", session);
            Dictionary<string, Attribute> prop = new Dictionary<string, Attribute>();
            foreach (string s in attr.Keys)
            {
                Hashtable ht = attr[s];
                foreach (string key in ht.Keys)
                    prop.Add(key, new Attribute(key, (Hashtable)ht[key], s, this.skillcheck, this));
            }

            Properties = prop;

            SetDependentProperties();
            UpdateDependentProperties(this.dependentProperties);
        }
        public void Load(string name, string filename, Session session)
        {
            string templateFile = filename;

            this.CSSStyle = XMLReader.GetValueFrom(templateFile, "sheet system CSSstyle", session)["value"].ToString();
            //this.layout = new CharacterSheet(XMLReader.GetValueFrom(templateFile, "sheet system layout", session)["value"].ToString());
            this.skillcheck = XMLReader.GetValueFrom(templateFile, "sheet system skillcheck", session)["value"].ToString();

            Dictionary<string, Hashtable> attr = XMLReader.GetValuesFrom(templateFile, "sheet section", session);
            Dictionary<string, Attribute> prop = new Dictionary<string, Attribute>();
            foreach (string s in attr.Keys)
            {
                Hashtable ht = attr[s];
                foreach (string key in ht.Keys)
                    prop.Add(key, new Attribute(key, (Hashtable)ht[key], s, this.skillcheck, this));
            }

            Properties = prop;

            SetDependentProperties();
            UpdateDependentProperties(this.dependentProperties);
        }
        #endregion

        #region Character Creation
        public bool UpdateBaseProperty(KeyValuePair<string, string> dict)
        {
            try
            {
                Properties[dict.Key].ModBase(dict.Value);
                return true;
            }
            catch (Exception e)
            {
                throw new NullReferenceException(dict.Key + "is not a valid character property.", e);
            }
        }
        public void UpdateBaseProperties(Dictionary<string, string> input)
        {
            foreach (KeyValuePair<string, string> kvp in input)
                if (UpdateBaseProperty(kvp))
                    continue;
        }
        public bool UpdateModProperty(KeyValuePair<string, string> dict)
        {
            try
            {
                Properties[dict.Key].ModMod(dict.Value);
                return true;
            }
            catch (Exception e)
            {
                throw new NullReferenceException(dict.Key + "is not a valid character property.", e);
            }
        }
        public void UpdateModProperties(Dictionary<string, string> input)
        {
            foreach (KeyValuePair<string, string> kvp in input)
                if (UpdateBaseProperty(kvp))
                    continue;
        }
        private void SetDependentProperties()
        {
            List<string> list = new List<string>();
            foreach (KeyValuePair<string, Attribute> kvp in Properties)
                if (!kvp.Value.SetBase())
                    list.Add(kvp.Key);

            this.dependentProperties = new List<string>(list);
        }
        private void UpdateDependentProperties(List<string> list)
        {
            do
            {
                List<string> list1 = new List<string>();
                foreach (string key in list)
                    if (Properties[key].SetBase())
                        list1.Add(key);
                foreach (string key in list1)
                    list.Remove(key);
            } while (list.Count > 0);
        }
        public bool AddProperty(Hashtable ht)
        {
            try
            {
                Dictionary<string, Attribute> dict = Properties;

                string section = ht["section"].ToString();
                ht.Remove(section);
                Attribute a = new Attribute(ht["id"].ToString(), ht, section, this.skillcheck, this);
                a.SetBase();
                dict.Add(ht["id"].ToString(), a);

                Properties = dict;

                return true;
            }
            catch (Exception e)
            {
                throw new Exception("Couldn't add property to character.", e);
            }
        }
        public bool AddProperties(Dictionary<string, Hashtable> attr)
        {
            foreach (KeyValuePair<string, Hashtable> kvp in attr)
            {
                try
                {
                    Dictionary<string, Attribute> dict = Properties;

                    string section = kvp.Value["section"].ToString();
                    kvp.Value.Remove(section);
                    Attribute a = new Attribute(kvp.Key, kvp.Value, section, this.skillcheck, this);
                    a.SetBase();
                    dict.Add(kvp.Key, a);

                    Properties = dict;
               }
                catch (Exception e)
                {
                    throw new Exception("Couldn't add property to character.", e);
                }
            }
            return true;
        }
        #endregion

        #region Seralization
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("_id", _id);
            info.AddValue("properties", Properties);
            info.AddValue("dependentProperties", dependentProperties);
            info.AddValue("CSSStyle", CSSStyle);
            info.AddValue("skillcheck", skillcheck);
        }
        #endregion

        #region Actions
        public ActionEventRequest GetEventRequest(string attribute)
        {
            return GetEventRequest(attribute, "");
        }
        public ActionEventRequest GetEventRequest(string attribute, string target)
        {
            ActionEventRequest aer = new ActionEventRequest();
            Attribute attr;
            string type;

            if (this.Properties[attribute].Get("combat").Value != "false" && target != "" && target.ToLower() != "damage")
            {
                attr = (this.GetAttributes("combat"))[attribute];

                type = attr.Get("type").Value; 
                string subType = attr.Get("ctype").Value;
                switch (subType)
                {
                    case "Melee":
                    case "Ranged":
                    case "Magic":
                        aer.SubType = "Attack:" + attr.Get("type").Value + ":" + attr.Get("attr").Value;
                        break;
                    case "Defense":
                        aer.SubType = subType + ":" + attr.Get("type").Value + ":" + attr.Get("attr").Value;
                        break;
                    case "Move":
                        aer.SubType = "Move:" + attr.Get("type").Value + ":" + attr.Get("attr").Value;
                        break;
                    default:
                        aer.SubType = "Other:" + attr.Get("type").Value + ":" + attr.Get("attr").Value;
                        break;
                }
                aer.SkillCheck = this.GetActions("combat", aer.SubType.Split(':')[0])[attribute];
            }
            else if (target.ToLower() == "damage")
            {
                attr = (this.GetAttributes("combat"))[attribute];
                type = attr.Get("type").Value;
                aer.SubType = attribute;
                aer.SkillCheck = attr.Value;
            }
            else
            {
                attr = (this.GetAttributes(attribute, "skills"))[attribute];
                aer.SkillCheck = this.GetActions(attr.ID, attr.Section)[attribute];
                if (attr.Section == "attributes" || attr.Section == "derived_attributes")
                    aer.SubType = attr.ID;
                else
                    aer.SubType = attr.Get("type").Value + ":" + attr.Get("attr").Value;
            }

            aer.Targets = new List<string>();
            if (target != "" && target.ToLower() != "damage")
            {
                string[] targets = target.Split(' ');
                foreach (string tar in targets)
                    aer.Targets.Add(tar);
            }

            aer.Actor = this.ID;
            aer.MainType = attribute;
            int min = 0;
            if (target.ToLower() != "damage")
            {
                foreach (string str in aer.SkillCheck.Split('+'))
                    min += (str.Trim().StartsWith("d")) ? 1 : Int32.Parse(str);
            }
            else
            {
                aer.MainType = target;
                Int32 res;
                min = Int32.TryParse(aer.SkillCheck.Split('d')[0], out res) ? 1 : res;
            }

            aer.MinSkillCheck = min.ToString();

            return aer;
        }
        #endregion

        #region Status Properties
        #endregion

        #region Display Information
        public Dictionary<string, string> GetList(string p, string type)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            switch (type)
            {
                case "combat":
                    foreach (KeyValuePair<string, Attribute> a in Properties)
                        if (a.Value.Get("combat").Key != null && (a.Value.Get("combat").Value == "both" || a.Value.Get("combat").Value == "true"))
                            switch (p)
                            {
                                case "Attack":
                                    if (a.Value.Get("ctype").Value == "Melee" || a.Value.Get("ctype").Value == "Ranged")
                                        result.Add(a.Key, a.Value.Value);
                                    break;
                                case "Defense":
                                    if (a.Value.Get("ctype").Value == "Defense")
                                        result.Add(a.Key, a.Value.Value);
                                    break;
                                case "Move":
                                    if (a.Value.Get("ctype").Value == "Move")
                                        result.Add(a.Key, a.Value.Value);
                                    break;
                                default:
                                    if (a.Value.Get("ctype").Value == "Other")
                                        result.Add(a.Key, a.Value.Value);
                                    break;
                            }
                    break;
                case "skill":
                    foreach (KeyValuePair<string, Attribute> a in Properties)
                        if (a.Value.Get("type").Key != null && a.Value.Get("type").Value == p)
                            result.Add(a.Key, a.Value.Value);
                    break;
                default:
                    foreach (KeyValuePair<string, Attribute> a in Properties)
                        if (a.Value.Section == p)
                            result.Add(a.Key, a.Value.Value);
                    break;
            }
            return result;
        }
        private Dictionary<string, Attribute> GetAttributes(string type)
        {
            Dictionary<string, Attribute> result = new Dictionary<string, Attribute>();
            foreach (KeyValuePair<string, Attribute> a in Properties)
            {
                if (type.ToLower() == "combat")
                {
                    if (a.Value.Get("combat").Key == null)
                        continue;
                    else if (a.Value.Get("combat").Value == "both" || a.Value.Get("combat").Value == "true")
                        result.Add(a.Key, a.Value);
                }
                else if (a.Value.Section == type)
                    result.Add(a.Key, a.Value);
            }
            return result;
        }
        private Dictionary<string, Attribute> GetAttributes(string attr, string type)
        {
            Dictionary<string, Attribute> result = new Dictionary<string, Attribute>();
            foreach (KeyValuePair<string, Attribute> a in Properties)
            {
                if (a.Key == attr)
                    result.Add(a.Key, a.Value);
            }
            return result;
        }
        public KeyValuePair<string, string> Get(string p)
        {
            if (p.Split(' ').Length > 1)
            {
                string[] skills = p.Split(' ');

                if (Properties.ContainsKey(skills[0]))
                {
                    return Properties[skills[0]].Get();
                }
                else if (Properties.ContainsKey(p))
                {
                    return Properties[p].Get();
                }
                else
                {
                    return Properties[skills[1]].Get();
                }
            }
            else
            {
                if (Properties.ContainsKey(p))
                    return Properties[p].Get();
            }
            throw new NullReferenceException("Character Property doesn't exist.");
        }
        public bool HasAttribute(string p)
        {
            if (p != null)
                return Properties.ContainsKey(p);
            else
                return false;
        }
        public bool HasAttribute(string attr, string value)
        {
            return Properties[attr].Has(value);
        }
        public Dictionary<string, string> GetActions(string action, string type)
        {
            Dictionary<string, Attribute> commands = new Dictionary<string, Attribute>();
            if (type != "skills" && action != "combat")
            {
                commands = GetAttributes(type);
            }
            else
            {
                commands = GetAttributes(action);
            }
            Dictionary<string, string> result = new Dictionary<string, string>();
            if (type == "combat")
            {
                foreach (KeyValuePair<string, Attribute> kvp in commands)
                {
                    if (HasAttribute(kvp.Key, "ctype") && kvp.Value.Section != "weapons")
                    {
                        string attr = GetValues(kvp, "attr");
                        string value = GetValues(kvp, "skill");
                        result.Add(kvp.Key, attr + " + " + value);
                    }
                    else if(kvp.Value.Section == "weapons")
                    {
                        string attr = GetValues(kvp, "attr");
                        string type1 = GetValues(kvp, "type");
                        string type2 = "";
                        foreach (string typ in type1.Split(','))
                        {
                            if (commands.ContainsKey(typ))
                            {
                                type2 = GetValues(new KeyValuePair<string,Attribute>(typ, commands[typ]), "skill");
                                break;
                            }
                        }
                        result.Add(kvp.Key, attr + " + " + (type2 =="" ? "0" : type2));
                    }
                }
            }
            else
            {
                foreach (KeyValuePair<string, Attribute> kvp in commands)
                {
                    if (kvp.Value.Has("note") && type == "derived_attributes")
                    {
                        string attr = GetValues(kvp, "attr");
                        string value = GetValues(kvp, "skill");
                        result.Add(kvp.Key, value);
                    }
                    else if (kvp.Value.Section == "weapons")
                    {
                        string attr = GetValues(kvp, "attr");
                        string type1 = GetValues(kvp, "type");
                        string type2 = "";
                        foreach (string typ in type1.Split(','))
                        {
                            if (commands.ContainsKey(typ))
                            {
                                type2 = GetValues(new KeyValuePair<string, Attribute>(typ, commands[typ]), "skill");
                                break;
                            }
                        }
                        result.Add(kvp.Key, attr + " + " + (type2 == "" ? "0" : type2));
                    }
                    else
                    {
                        string attr = GetValues(kvp, "attr");
                        string value = GetValues(kvp, "skill");
                        result.Add(kvp.Key, attr + " + " + value);
                    }
                }
            }
            return result;
        }
        private string GetValues(KeyValuePair<string, Attribute> kvp, string id)
        {
            if (kvp.Value.Get(id).Value == "GM")
                //TODO: Provide interface to query GM for proper skillcheck.
                return "GM";
            else if (kvp.Value.Get(id).Value == "this")
                return kvp.Value.Value;
            else if (id == "type")
                return kvp.Value.Get(id).Value;
            else
            {
                string value = kvp.Value.Get(id).Value;

                if (HasAttribute(value))
                    return this.Get(value).Value;
                else if (HasAttribute(kvp.Key))
                    return this.Get(kvp.Key).Value;
            }
            throw new NullReferenceException("Skill has no selected base attributes.");
        }
        #endregion
    }
}
